package org.fto.jthink.ai.example.fg;

import java.util.*;

import org.fto.jthink.util.NumberHelper;
import org.fto.jthink.util.RandomHelper;
import org.fto.jthink.ai.nn.*;

/**
 * 电脑AI角色
 * 
 * @author wenjian
 */
public class AIActor extends Actor{
  
  /* 设置以多少场比赛作为一个统计单元 */
  int statNums=12;
  int randomNumIdx=0;
  /* 训练样本数 */
  int sampleNums=40;
  
  Brain brain = new Brain();
  Statistics statistics;
  public AIActor(Statistics statistics){
    this.statistics=statistics;
  }
  
  /**
   * 返回本次出拳
   */
  public String getHand() {
    if(!Status.useAI || randomNumIdx<statNums){
      randomNumIdx++;
      return random();
    }else{
      return brain.hand();
      //return String.valueOf(1);
    }
  }
  
  /**
   * 训练
   */
  public void learning(){
    brain.learning();
  }
  
  private String random(){
    return String.valueOf((RandomHelper.randomInt(3)+1));
  }
  
  /**
   * AI角色的大脑 
   */
  class Brain{
    NeuralNetwork nn;
    public Brain(){
      nn = NeuralNetwork.newNeuralNetwork();
      /* 输入神经元为对玩这出拳的初统计次数，一次出拳为一个输入神经元,以及在统计次数中，石头，剪子，布所占比率, */
      int inputLayerNums = statNums;
      //nn.addLayer(new Layer(statNums+3));
      nn.addLayer(new Layer(inputLayerNums));
      /* 隐蔽层数量为输入层数量*输出层数量/2 */
      nn.addLayer(new Layer((inputLayerNums*3)/2));
      /* 三个输出层，分别是预测玩家下一次分别有可能出（石头，剪子，布）的比率 */
      nn.addLayer(new Layer(3));
      nn.setLearningRate(0.3);//学习率
      nn.setUseMomentum(true, 0.9);//动量
    }
    
    public String hand(){
      /* 返回输入层 */
      List inNeuronses = nn.getInputLayer().getNeurons();
      
      /* 返回历史所有出拳，并加入到输入神经元 */
      List hands = statistics.getHands();
      int idx = hands.size();
      for(int i=statNums;i>0;i--){
        //System.out.println("idx-i:"+(idx-i));
        Object[] hand = (Object[])hands.get(idx-i);
        String pactorhand = (String)hand[0];
        //System.out.println("本统计单元中第"+(statNums-i)+"次出拳:"+pactorhand+"("+((Double.parseDouble(pactorhand)-1)/2)+")");
        ((Neuron)inNeuronses.get((statNums-i))).inputValue((Double.parseDouble(pactorhand)-1)/2); //hand[0]为玩家出拳
      }
      /* 思考 */
      nn.feedForward();
      
      /* 输出结果,返回玩家下一回出石头，剪子，布的概率  */
      List outNeuronses = nn.getOutputLayer().getNeurons();
      double rockProbty = ((Neuron)outNeuronses.get(0)).getValue();//石头
      double cutProbty = ((Neuron)outNeuronses.get(1)).getValue();//剪子
      double clothProbty = ((Neuron)outNeuronses.get(2)).getValue();//布
      
      System.out.println("石头:"+NumberHelper.formatNumber(rockProbty)+","+"剪子:"+NumberHelper.formatNumber(cutProbty)+","+"布:"+NumberHelper.formatNumber(clothProbty));
      return FGUtil.compareHand(rockProbty, cutProbty, clothProbty);
    }
    
    
    /**
     * 训练
     */
    public void learning(){
      double error=1;
      List sampleDatas = statistics.getSampleDatas(sampleNums, statNums+1);//提取最近的100个样本，每一个样本statNums+1个数据
      int sampleSize = sampleDatas.size();
      //System.out.println("sampleSize:"+sampleSize);
      if(sampleSize==0){
        return;
      }
      System.out.println("输到了度，是该冲冲电了!!!");
      List inputNeuronses = nn.getInputLayer().getNeurons();
      List outputNeuronses = nn.getOutputLayer().getNeurons();
      int i=0;
      //long stime = System.currentTimeMillis();
      for(;i<200 && error>0.02;i++){
        error=0;
        for(int j=0;j<sampleSize;j++){
          double[] data = (double[])sampleDatas.get(j);
          for(int p=0;p<statNums;p++){
            ((Neuron)inputNeuronses.get(p)).inputValue(data[p]);
          }
          double formatedHand = data[data.length-1];//最后一个样本数据为正确的样本输出值，即玩家历史出拳,已经被格式化成（0,0.5,1）
          if(formatedHand<0.01){//是石头
            ((Neuron)outputNeuronses.get(0)).setDesiredValue(0.99);
            ((Neuron)outputNeuronses.get(1)).setDesiredValue(0.01);
            ((Neuron)outputNeuronses.get(2)).setDesiredValue(0.01);
          }else if(formatedHand<0.51){//是剪子
            ((Neuron)outputNeuronses.get(0)).setDesiredValue(0.01);
            ((Neuron)outputNeuronses.get(1)).setDesiredValue(0.99);
            ((Neuron)outputNeuronses.get(2)).setDesiredValue(0.01);
          }else{//布
            ((Neuron)outputNeuronses.get(0)).setDesiredValue(0.01);
            ((Neuron)outputNeuronses.get(1)).setDesiredValue(0.01);
            ((Neuron)outputNeuronses.get(2)).setDesiredValue(0.99);
          }
          nn.feedForward();
          error+=nn.calculateError();
          nn.backPropagate();
        }
        error = error/sampleSize;
        
      }
      System.out.println("学习效果:"+error+"(值越小越好)");
    }
    
    
  }
  
}
